﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace CelsiusAviSynth
{
	/// <summary>
	/// 提供AVS数据相关实例函数
	/// </summary>
	/// <remarks>请配合AvisynthWrapper.dll使用</remarks>
	public class AviSynthProvider
	{
		/// <summary>
		/// 非托管函数，释放AVS文件
		/// </summary>
		/// <param name="avs">AVS信息指针</param>
		[DllImport("AvisynthWrapper", CharSet = CharSet.Ansi, ExactSpelling = true)]
		private extern static int dimzon_avs_destroy(ref IntPtr avs);
		/// <summary>
		/// 非托管函数，获得AVS错误
		/// </summary>
		/// <param name="avs">AVS信息指针</param>
		[DllImport("AvisynthWrapper", CharSet = CharSet.Ansi, ExactSpelling = true)]
		private extern static int dimzon_avs_getlasterror(IntPtr avs, [MarshalAs(UnmanagedType.LPStr)] StringBuilder sb, int len);
		/// <summary>
		/// 得到目标帧数据
		/// </summary>
		/// <param name="avs">AVS信息指针</param>
		/// <param name="buf">缓存数组（容量必须为帧跨距×帧高度）</param>
		/// <param name="stride">帧跨距</param>
		/// <param name="frm">帧序号</param>
		[DllImport("AvisynthWrapper", CharSet = CharSet.Ansi, ExactSpelling = true)]
		private static extern int dimzon_avs_getvframe(IntPtr avs, byte[] buf, int stride, int frm);
		/// <summary>
		/// 得到目标帧数据
		/// </summary>
		/// <param name="avs">AVS信息指针</param>
		/// <param name="buf">缓存指针</param>
		/// <param name="stride">帧跨距</param>
		/// <param name="frm">帧序号</param>
		[DllImport("AvisynthWrapper", CharSet = CharSet.Ansi, ExactSpelling = true)]
		private static extern int dimzon_avs_getvframe(IntPtr avs, IntPtr buf, int stride, int frm);
		/// <summary>
		/// 非托管函数，初始化AVS信息
		/// </summary>
		/// <param name="avs">AVS信息指针</param>
		/// <param name="func">AVS函数（详见Avisynth文档）</param>
		/// <param name="arg">AVS函数参数</param>
		/// <param name="vi">视频音频信息</param>
		/// <param name="originalColorspace"></param>
		[DllImport("AvisynthWrapper", CharSet = CharSet.Ansi, ExactSpelling = true)]
		private static extern int dimzon_avs_init(ref IntPtr avs, string func, string arg, ref AVSDLLInfo vi, ref AviSynthColorspace originalColorspace, ref int originalSampleType, string cs);

		/// <summary>
		/// AVS文件指针
		/// </summary>
		private IntPtr _avs = IntPtr.Zero;
		/// <summary>
		/// 该AVS文件信息
		/// </summary>
		private AVSDLLInfo _vi;

		/// <summary>
		/// 当前帧类型（关键、空白、一般）
		/// </summary>
		public FrameType CurrentFrameType
		{
			get
			{
				return FrameType.Key;
			}
		}

		/// <summary>
		/// 该视频尺寸
		/// </summary>
		public System.Drawing.Size Size
		{
			get
			{
				return new System.Drawing.Size(_vi.width, _vi.height);
			}
		}

		/// <summary>
		/// 该视频所含帧数
		/// </summary>
		public int FrameCount
		{
			get
			{
				return _vi.num_frames;
			}
		}

		/// <summary>
		/// 该视频帧速率
		/// </summary>
		public double FrameRate
		{
			get
			{
				return (double)_vi.raten / (double)_vi.rated;
			}
		}

#if XNA
		/// <summary>
		/// 得到当前帧数据
		/// </summary>
		/// <returns>byte数组，容量必须为视频跨距×视频高度</returns>
		public byte[] GetFrame()
		{
			int stride = 0;
			switch (_vi.pixel_type)
			{
				case AviSynthColorspace.RGB24:
					stride = _vi.width * 3;
					break;
				case AviSynthColorspace.RGB32:
					stride = _vi.width * 4;
					break;
				default:
					ThrowAvsExecption();
					break;
			}
			byte[] t = new byte[stride * _vi.height];
			byte[] tempbits = new byte[stride * _vi.height];

			dimzon_avs_getvframe(_avs, tempbits, stride, this.CurrentFrameNumber);

			//返回颜色GBRA（32bits），而Dirext显示需要RGBA（32bits），以下为转换；
			for (int y = 0; y < _vi.height; y++)
			{
				//RGB存储为上下颠倒，是否颠倒；
				int newy = y;//				_vi.height. - y;
				for (int x = 0; x < stride; x += 4)
				{
					t[x + 2 + stride * newy] = tempbits[(x + 0 + stride * y)];
					t[x + 1 + stride * newy] = tempbits[(x + 1 + stride * y)];
					t[x + 0 + stride * newy] = tempbits[(x + 2 + stride * y)];
					t[x + 3 + stride * newy] = 255;
				}
			}
			return t;
		}
#else
		/// <summary>
		/// 得到当前帧数据
		/// </summary>
		/// <param name="output">位图信息</param>
		public void GetFrame(ref System.Drawing.Bitmap output)
		{
			if (output == null || output.PixelFormat != PixelFormat.Format24bppRgb ||
				output.Width != _vi.width || output.Height != _vi.height)
			{
				if (output != null)
				{
					output.Dispose();
				}
				output = new Bitmap(_vi.width, _vi.height, PixelFormat.Format24bppRgb);
			}
			BitmapData bitmapData = output.LockBits(new System.Drawing.Rectangle(0, 0, output.Width, output.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
			try
			{
				//always bottom-up bitmap
				if (dimzon_avs_getvframe(_avs, new IntPtr(bitmapData.Scan0.ToInt64() + bitmapData.Stride * (output.Height - 1)), -bitmapData.Stride, CurrentFrameNumber) != 0)
				{
					ThrowAvsExecption();
				}
			}
			finally
			{
				output.UnlockBits(bitmapData);
			}
		}
#endif

		/// <summary>
		/// 打开AVS文件
		/// </summary>
		/// <param name="filePath">输入文件路径</param>
		public void Open(string filePath)
		{
			Cleanup();
			if (!File.Exists(filePath))
			{
				throw new FileNotFoundException();
			}
			AviSynthColorspace cs = new AviSynthColorspace();
			int sr = new int();
			if (dimzon_avs_init(ref _avs, "Import", filePath, ref _vi, ref cs, ref sr, AviSynthColorspace.RGB24.ToString()) != 0)
			{
				ThrowAvsExecption();
			}
		}

		/// <summary>
		/// 释放AVS文件
		/// </summary>
		private void Cleanup()
		{
			if (_avs != IntPtr.Zero)
			{
				dimzon_avs_destroy(ref _avs);
				_avs = IntPtr.Zero;
			}
		}

		/// <summary>
		/// 抛出AVS错误
		/// </summary>
		private void ThrowAvsExecption()
		{
			StringBuilder sb = new StringBuilder(0x400);
			sb.Length = dimzon_avs_getlasterror(_avs, sb, 0x400);
			Cleanup();
			throw new InvalidDataException("Error AVS Data");
		}

		/// <summary>
		/// 当前帧帧号
		/// </summary>
		public int CurrentFrameNumber
		{
			get;
			set;
		}

		/// <summary>
		/// 该视频高度
		/// </summary>
		public int Height
		{
			get
			{
				return _vi.height;
			}
		}

		/// <summary>
		/// 该视频宽度
		/// </summary>
		public int Width
		{
			get
			{
				return _vi.width;
			}
		}

	}
}
